/**
 * @file   ParserDate.java
 * @author Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Thu Aug 23 16:57:14 2007
 * 
 * @brief  
 * 
 * 
 */

package sim.utils.parser;

import sim.utils.*;
import java.util.Calendar;
import java.util.Date;

public class ParserDate implements ParserScan {

    public static final int DAY = 0;
    public static final int MONTH = 1;
    public static final int YEAR = 2;
    public static final int HOUR = 3;
    public static final int MINUTE = 4;
    public static final int SECOND = 5;
    public static final int FIELDS = 6;

    private static final int[] CALENDAR_FIELD = {Calendar.DAY_OF_MONTH, 
						 Calendar.MONTH,
						 Calendar.YEAR,
						 Calendar.HOUR_OF_DAY,
						 Calendar.MINUTE,
						 Calendar.SECOND};

    private static final byte[] TOKEN_DAY = {'/', '-', ' '};
    private static final byte[] TOKEN_HOUR = {':', ' '};

//     private Parser m_ParserData = new Parser(this, Utils.TOKEN, Utils.TOKEN_SPACE, 1, Parser.FORWARD | Parser.PATTERN);
    private Parser m_ParserDate = new Parser(this);
    private int m_ParserFor;
    private int m_ParserField;

    // 1 de Enero de 1970 a las 0 horas
    private int[] m_Fields = {1, 0, 1970, 0, 0, 0};

    /** 
     * Devuelve el valor de un campo de la fecha.
     * 
     * @param field Identificador del campo. Este valor puede ser 
     *              ParserDate::DAY, ParserDate::MONTH, ParserDate::YEAR
     *              ParserDate::HOUR, ParserDate::MINUTE, ParserDate::SECOND.
     * 
     * @return Devuelve un valor entero correspondiente al campo de la fecha.
     */    
    public int get(int field) {
	if ((field > -1) && (field < FIELDS)) {
	    return ((field == ParserDate.MONTH) ? m_Fields[field] + 1 : m_Fields[field]);
	}
	return 0;
    }

    /** 
     * Establece el valor para uno de los campos de la fecha.
     * 
     * @param field Identificador de campo.
     * @param value Valor a establecer.
     *
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
    public boolean set(int field, int value) {

	switch (field) {
	case ParserDate.DAY:
	    m_Fields[ParserDate.DAY] = (((value > 0) && (value < 32)) ? 
					value : 
					m_Fields[ParserDate.HOUR]);
	    break;
	case ParserDate.MONTH:
	    value--;
	    m_Fields[ParserDate.MONTH] = (((value > -1) && (value < 12)) ? 
					  value : 
					  m_Fields[ParserDate.MONTH]);
	    break;
	case ParserDate.YEAR:
	    value = (((value > 69) && (value < 100)) ? value + 1900 : value);
	    m_Fields[ParserDate.YEAR] = (value > 1969 ? value : m_Fields[ParserDate.YEAR]);
	    break;
	case ParserDate.HOUR:
	    m_Fields[ParserDate.HOUR] = (((value > -1) && (value < 24)) ? 
					 value : 
					 m_Fields[ParserDate.HOUR]);
	    break;
	case ParserDate.MINUTE:
	    m_Fields[ParserDate.MINUTE] = (((value > -1) && (value < 60)) ? 
					   value : 
					   m_Fields[ParserDate.MINUTE]);
	    break;
	case ParserDate.SECOND:
	    m_Fields[ParserDate.SECOND] = (((value > -1) && (value < 60)) ? 
					   value : 
					   m_Fields[ParserDate.SECOND]);
	    break;
	default:
	    // Dont't worry be hacker!.
	    field = 0;
	    value = -1;
	}

	return (m_Fields[field] == value);
    }

    /** 
     * Devuelve el número de segundos transcurridos del el 1 enero 1970
     * 
     * 
     * @return Devuelve un valor entero.
     */
    public int getDayInSeconds() {
	int i;
	Date date;
	Calendar cal = Calendar.getInstance();
	for (i = 0; i < ParserDate.FIELDS; i++) {
	    cal.set(ParserDate.CALENDAR_FIELD[i], m_Fields[i]);
	}
	date = cal.getTime();
	return ((int)(date.getTime()/1000));
    }

    /** 
     * Devuelve el número de segundos transcurridos desde la media noche.
     * 
     * 
     * @return Devuelve un valor entero.
     */
    public int getHourInSeconds() {
	int ret;
	ret = m_Fields[ParserDate.SECOND];
	ret += m_Fields[ParserDate.MINUTE]*60;
	ret += m_Fields[ParserDate.HOUR]*60*60;
	return ret;
    }

    /** 
     * Busca el día, mes, año, hora, minutos, y segundos en una representación de fecha. 
     * 
     * @param day Arreglo de bytes que contiene un representación de fecha.
     * @param start Private byte válido del arreglo date.
     * @param length Longitud de bytes válidos del arreglo date.
     * 
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
//     public boolean scan(final byte[] date, int start, int length) {
// 	m_ParserFor = ParserDate.FIELDS;
// 	return m_ParserData.scan(date, start, length);
//     }

    /** 
     * Busca el día, mes y año en una representación de fecha del día.
     * 
     * @param day Arreglo de bytes que contiene un representación de fecha del dia.
     * @param start Private byte válido del arreglo day.
     * @param length Longitud de bytes válidos del arreglo day.
     * 
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
    public boolean scanDay(final byte[] day, int start, int length) {
	m_ParserFor = ParserDate.DAY;
	m_ParserField = ParserDate.DAY;
	m_ParserDate.init(ParserDate.TOKEN_DAY, 
			  0, 
			  ParserDate.TOKEN_DAY.length, 
			  Parser.FORWARD | Parser.PATTERN);
	return m_ParserDate.scan(day, start, length);
    }

    /** 
     * Busca el día, mes y año en una representación de fecha del día.
     * 
     * @param day Arreglo de bytes que contiene un representación de fecha del dia.
     * @param start Private byte válido del arreglo day.
     * 
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
    public boolean scanDay(final byte[] day, int start) {
	return scanDay(day, start, day.length - start);
    }

    /** 
     * Busca el día, mes y año en una representación de fecha del día.
     * 
     * @param day Cadena de caracteres que contiene un representación de fecha del dia.
     * 
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
    public boolean scanDay(final String day) {
	byte[] bytes;
	bytes = day.getBytes();
	return scanDay(bytes, 0, bytes.length);
    }

    /** 
     * Busca la hora, minutos, y segundos en una representación de hora.
     * 
     * @param hour Arreglo de bytes que contiene una representación de hora.
     * @param start Primer byte válido del arreglo hour.
     * @param length Longitud de bytes válidos del arreglo hour.
     * 
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
    public boolean scanHour(final byte[] hour, int start, int length) {
	m_ParserFor = ParserDate.HOUR;
	m_ParserField = ParserDate.HOUR;
	m_ParserDate.init(ParserDate.TOKEN_HOUR, 
			  0, 
			  ParserDate.TOKEN_HOUR.length, 
			  Parser.FORWARD | Parser.PATTERN);
	return m_ParserDate.scan(hour, start, length);
    }

    /** 
     * Busca la hora, minutos, y segundos en una representación de hora.
     * 
     * @param hour Arreglo de bytes que contiene una representación de hora.
     * @param start Primer byte válido del arreglo hour.
     * 
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
    public boolean scanHour(final byte[] hour, int start) {
	return scanHour(hour, start, hour.length - start);
    }

    /** 
     * Busca la hora, minutos, y segundos en una representación de hora.
     * 
     * @param hour Cadena de caracteres que contiene una representación de hora.
     * 
     * @return Si tiene éxito devuelve true, en caso contrario devuelve false.
     */
    public boolean scanHour(final String hour) {
	byte[] bytes;
	bytes = hour.getBytes();
	return scanHour(bytes, 0, bytes.length);
    }

    public final boolean parserHit(int hit, final byte[] line, int start, int length, Object something) {
	boolean ret = false;
	switch(m_ParserFor) {
	case ParserDate.FIELDS:
	    ret = parserHitDay(hit, line, start, length, something);
	    break;
	case ParserDate.DAY:
	    ret = parserHitDay(hit, line, start, length, something);
	    break;
	case ParserDate.HOUR:
	    ret = parserHitHour(hit, line, start, length, something);
	    break;
	}
	return ret;
    };

//     private boolean parserHitData(int hit, final byte[] line, int start, int length, Object something) {
// 	return true;
//     }

    private boolean parserHitDay(int hit, final byte[] line, int start, int length, Object something) {
	int value;
	if (length > 0) {
	    value = Utils.memtoul(line, start, length, 10);
	    switch (m_ParserField) {
	    case ParserDate.DAY:
		if (set(ParserDate.DAY, value)) {
		    m_ParserField = ParserDate.MONTH;
		}
		break;
	    case ParserDate.MONTH:
		if (set(ParserDate.MONTH, value)) {
		    m_ParserField = ParserDate.YEAR;
		}
		break;
	    case ParserDate.YEAR:
		set(ParserDate.YEAR, value);
		break;
	    }
	}

	return true;
    }

    private boolean parserHitHour(int hit, final byte[] line, int start, int length, Object something) {
	int value;
	if (length > 0) {
	    value = Utils.memtoul(line, start, length, 10);
	    switch (m_ParserField) {
	    case ParserDate.HOUR:
		if (set(ParserDate.HOUR, value)) {
		    m_ParserField = ParserDate.MINUTE;
		}
		break;
	    case ParserDate.MINUTE:
		if (set(ParserDate.MINUTE, value)) {
		    m_ParserField = ParserDate.SECOND;
		}
		break;
	    case ParserDate.SECOND:
		set(ParserDate.SECOND, value);
	    }
	}
	return true;
    }

    public final boolean parserEnd(boolean cancel) {
	boolean ret;
	ret = false;
	switch(m_ParserFor) {
	case ParserDate.FIELDS:
	    ret = true;
	    break;
	case ParserDate.DAY:
	    // Por lo menos el día
	    ret = (m_ParserField >= ParserDate.DAY);
	    break;
	case ParserDate.HOUR:
	    // Por lo menos la hora.
	    ret = (m_ParserField >= ParserDate.HOUR);
	    break;
	}
	return ret;
    }
}
